﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Diagnostics;

namespace Master
{
    /**
     * Spawnpoint can be used to mark a specific lane as a 'spawnlane' on which you can easily add new cars.
     * Just create a spawnpoint, locate it on top of a lane somewhere and pass the spawnpoint to the carmanager.addCar-method.
     * A new car will be created just before the beginning of the above mentioned lane.
     * The orientation of the lane will be used as orientation for the newly created car.
     * 
     * @Author A.J. Janssen
     * @Changes made by:
     *      - T.T. Dusseldorp
     * */
    public class SpawnPoint : Control
    {
        private Point controlLocation;
        private SpawnpointState currentState;
        private UserControl addCarControl, pendingConnectionControl, connectedControl, activeControl;

        /**
         * Method to associate this spawnpoint with a specific addcar-control
         * */
        public void setAddCarControl(CtrlAddCar addCarControl)
        {
            addCarControl.setSpawnPoint(this);
            this.addCarControl = addCarControl;
            activeControl = this.addCarControl;
            this.controlLocation = new Point(Map.GetLeft(addCarControl), Map.GetTop(addCarControl));
            currentState = SpawnpointState.DEFAULT;
            createControls();
        }

        private void createControls() //method to create connection-related controls for this spawnpoint
        {
            connectedControl = new CtrlConnected(this);
            initControl(connectedControl);
            pendingConnectionControl = new CtrlPendingConnection(this);
            initControl(pendingConnectionControl);
        }

        private void initControl(UserControl control) //method to initialise the given control (this will properly locate the control and hide it untill it is needed)
        {
            Map.INSTANCE.Children.Add(control);
            Map.SetLeft(control, controlLocation.X);
            Map.SetTop(control, controlLocation.Y);
            control.Visibility = System.Windows.Visibility.Hidden;
        }

        /**
         * Method to accept a connection and associate it with this spawnpoint
         * */
        public void acceptConnection()
        {
            NetworkManager.INSTANCE.acceptConnection(this);
        }

        /**
         * Method to close the connection associated with this spawnpoint
         * */
        public void closeConnection()
        {
            if (currentState != SpawnpointState.CONNECTED) 
                return;
            setState(SpawnpointState.DEFAULT);
            NetworkManager.INSTANCE.closeConnection(this);
        }

        /**
         * Method to change the state of this spawnpoint
         * */
        public void setState(SpawnpointState state)
        {
            activeControl.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
            new Action(delegate()
            {
                //if no state change --> do nothing
                if (state == currentState)
                    return;
                //hide currently active control
                activeControl.Visibility = System.Windows.Visibility.Collapsed;
                //set new active control
                switch (state)
                {
                    case SpawnpointState.DEFAULT: activeControl = addCarControl; break;
                    case SpawnpointState.PENDING: activeControl = pendingConnectionControl; break;
                    case SpawnpointState.CONNECTED: activeControl = connectedControl; break;
                }
                //make new active control visible
                activeControl.Visibility = System.Windows.Visibility.Visible;
                //update current state
                currentState = state;
            }));
        }

        /**
         * Method to retrieve the current state of this spawnpoint
         * */
        public SpawnpointState getState()
        {
            return currentState;
        }

        /**
         * Method to retrieve the exact location new cars should spawn on. 
         * This will generate a centered location just before the beginning of the relevant lane.
         * (Newly spawned cars will have to be centered on this point)
         * */
        public Point getPoint(Car car)
        {
            Lane lane = getLane();
            Point lanePoint = Toolkit.getPointRelativeToMap(lane);
            Point point = new Point();
            switch (lane.Orientation)
            {
                case 0:
                    point.X = ((lanePoint.X * 2) + lane.ActualWidth) / 2;
                    point.Y = lanePoint.Y + lane.ActualHeight + car.getTrueBounds().Height;
                    break;
                case 90:
                    point.X = lanePoint.X - car.getTrueBounds().Width;
                    point.Y = ((lanePoint.Y * 2) + lane.ActualHeight) / 2;
                    break;
                case 180:
                    point.X = ((lanePoint.X * 2) + lane.ActualWidth) / 2;
                    point.Y = lanePoint.Y - car.getTrueBounds().Height;
                    break;
                case 270:
                    point.X = lanePoint.X + lane.ActualWidth + car.getTrueBounds().Width;
                    point.Y = ((lanePoint.Y * 2) + lane.ActualHeight) / 2;
                    break;
            }
            return point;
        }

        /**
         * Method to retrieve the lane associated with this spawnpoint.
         * */
        public Lane getLane()
        {
            Lane lane = Map.INSTANCE.getLaneAtPoint(Toolkit.getCenterPoint(this));
            if (lane == null)
                throw new InvalidOperationException("Spawnpoint not located on lane");
            return lane;
        }
    }
}
